package top.lshaci.learning;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

/**
 * MergeTest
 *
 * @author lshaci
 * @since 1.0.0
 */
public class MergeTest {

    public static void main(String[] args) {
        List<Map<Long, String>> list1 = new ArrayList<>();
        Map<Long, String> map1 = new HashMap<>();
        map1.put(1L, "(科目 开始以 [1011,1002])");
        map1.put(2L, "(项目名称 开始以 [材料科研])");
        map1.put(3L, "(对方名称 包含 [技术])");
        Map<Long, String> map2 = new HashMap<>();
        map2.put(1L, "(科目名称 包含 [材料费])");
        map2.put(3L, "(对方名称 包含 [有限])");

        list1.add(map1);
        list1.add(map2);
        TimeInterval timer = DateUtil.timer();

        final Map<Long, String> merge = mergeMap(1, list1);
        final long interval = timer.interval();//花费毫秒数
        System.err.println("使用Map花费：" + interval + "ms");
        System.err.println(merge);

        List<Set<Model>> list2 = new ArrayList<>();
        Set<Model> set1 = new HashSet<>();
        set1.add(new Model(1L, "(科目 开始以 [1011,1002])"));
        set1.add(new Model(2L, "(项目名称 开始以 [材料科研])"));
        set1.add(new Model(3L, "(对方名称 包含 [技术])"));
        Set<Model> set2 = new HashSet<>();
        set2.add(new Model(1L, "(科目名称 包含 [材料费])"));
        set2.add(new Model(3L, "(对方名称 包含 [有限])"));

        list2.add(set1);
        list2.add(set2);
        timer.intervalRestart();//返回花费时间，并重置开始时间
        final Set<Model> merge1 = merge(1, list2);
        System.err.println("使用对象花费: " + timer.interval() + "ms");
        System.err.println(merge1);

        timer.intervalRestart();
        final Map<Long, String> merge3 = mergeMap(0, list1);
        System.err.println("使用Map花费2：" + interval + "ms");
        System.err.println(merge3);

        timer.intervalRestart();//返回花费时间，并重置开始时间
        final Set<Model> merge4 = merge(0, list2);
        System.err.println("使用对象花费4: " + timer.interval() + "ms");
        System.err.println(merge4);
    }

    public static Map<Long, String> mergeMap(int type, List<Map<Long, String>> data) {
        Map<Long, String> result = new HashMap<>();
        if (CollUtil.isEmpty(data)) {
            return result;
        }
        BinaryOperator<HashSet<Long>> operator;
        String delimiter;
        if (type == 0) {
            operator = (a, b) -> {
                a.retainAll(b);
                return a;
            };
            delimiter = " and ";
        } else {
            operator = (a, b) -> {
                a.addAll(b);
                return a;
            };
            delimiter = " or ";
        }
        Optional<HashSet<Long>> optional = data.stream().map(m -> {
            final HashSet<Long> objects = new HashSet<>();
            objects.addAll(m.keySet());
            return objects;

        }).reduce(operator);
        if (!optional.isPresent()) {
            return result;
        }
        Set<Long> ids = optional.get();
        if (ids.isEmpty()) {
            return result;
        }
        Map<Long, List<String>> r = new HashMap<>();
        for (Map<Long, String> map : data) {
            map.forEach((k, v) -> {
                if (ids.contains(k)) {
                    List<String> reason = Optional.ofNullable(r.get(k)).orElseGet(ArrayList::new);
                    reason.add(v);
                    r.put(k, reason);
                }
            });
        }
        for (Long id : ids) {
            final List<String> strings = r.get(id);
            String reason;
            if (strings.size() > 1) {

                reason = strings.stream().collect(Collectors.joining(delimiter, "(", ")"));
            } else {
                reason = strings.get(0);
            }
            result.put(id, reason);
        }
        return result;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static
    class Model {
        Long id;
        String reason;
    }

    public static Set<Model> merge(int type, List<Set<Model>> data) {
        Set<Model> result = new HashSet<>();
        if (CollUtil.isEmpty(data)) {
            return result;
        }
        BinaryOperator<Set<Long>> operator;
        String delimiter;
        if (type == 0) {
            operator = (a, b) -> {
                a.retainAll(b);
                return a;
            };
            delimiter = " and ";
        } else {
            operator = (a, b) -> {
                a.addAll(b);
                return a;
            };
            delimiter = " or ";
        }
        Optional<Set<Long>> optional = data.stream().map(ms ->
                ms.stream().map(Model::getId).collect(Collectors.toSet())

        ).reduce(operator);
        if (!optional.isPresent()) {
            return result;
        }
        Set<Long> ids = optional.get();
        if (ids.isEmpty()) {
            return result;
        }
        Map<Long, List<String>> r = new HashMap<>();
        for (Set<Model> models : data) {
            models.forEach(m -> {
                if (ids.contains(m.id)) {
                    List<String> reason = Optional.ofNullable(r.get(m.id)).orElseGet(ArrayList::new);
                    reason.add(m.getReason());
                    r.put(m.id, reason);
                }
            });
        }
        for (Long id : ids) {
            final List<String> strings = r.get(id);
            String reason;
            if (strings.size() > 1) {

                reason = strings.stream().collect(Collectors.joining(delimiter, "(", ")"));
            } else {
                reason = strings.get(0);
            }
            Model m = new Model();
            m.setId(id);
            m.setReason(reason);
            result.add(m);
        }
        return result;
    }

}
